#pragma once
#include<iostream>
#include<string>
#include <fstream>
#include<assert.h>
#include <algorithm>
#include "../comm/httplib.h"
#include"../comm/log.hpp"
#include"../comm/util.hpp"
#include"oj_model.hpp"
#include"oj_view.hpp"
#include <mutex>

namespace ns_control
{
    using namespace std;
    using namespace ns_model;
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_view;
    using namespace httplib;



    const std::string service_machine = "./conf/service_machine.conf";


    class Machine
    {
    public:
        string ip;
        int port;
        uint64_t load;
        mutex *mtx;//mutex 禁止拷贝，只能用指针
    public:
        void IncLoad()
        {
            if(mtx)
            {
                mtx->lock();
            }
            load++;
            if(mtx)
            {
                mtx->unlock();
            }
        }

        void DecLoad()
        {
            if(mtx)
            {
                mtx->lock();
            }
            load--;
            if(mtx)
            {
                mtx->unlock();
            }
        }
        uint64_t Load()
        {
            uint64_t _load = 0;
            if (mtx) mtx->lock();
            _load = load;
            if (mtx) mtx->unlock();

            return _load;
        }
        void ResetLoad()
        {
            if(mtx) mtx->lock();
            load = 0;
            if(mtx) mtx->unlock();
        }
        
        
    };



    class LoadBlance
    {
    private:
        // 可以给我们提供编译服务的所有的主机
        // 每一台主机都有自己的下标，充当当前主机的id
        std::vector<Machine> machines;
        // 所有在线的主机id
        std::vector<int> online;
        // 所有离线的主机id
        std::vector<int> offline;
        // 保证LoadBlance它的数据安全
        std::mutex mtx;

    public:
        LoadBlance()
        {
            assert(LoadConf(service_machine));
           
        }
        ~LoadBlance()
        {
        }

    public:
        bool LoadConf(const std::string &machine_conf)
        {
            ifstream in(machine_conf);
            if (!in.is_open())
            {
                FATAL_Log("加载主机配置文件失败");
                return false;
            }
            std::string line; 
            string sep=":";
            while (std::getline(in, line))
            {
                std::vector<std::string> v1;
               
                StringUtil::split(line,sep,v1);
                
                if(v1.size()!=2)
                {
                    ERR_Log("切分%s失败",line.c_str());
                    std::cout<<v1.size()<<std::endl;
                    std::cout<<v1[0];
                    continue;
                }
                Machine m;
                m.ip=v1[0];
                m.port=stoi(v1[1]);
                m.load=0;
                m.mtx = new std::mutex();
                online.push_back(machines.size());
                machines.push_back(m);
            }
            in.close();
            return true;


        }

        bool SmartChoice(int *id, Machine **m)
        {
            mtx.lock();
            int num=online.size();
            if (num == 0)
            {
                mtx.unlock();
               FATAL_Log("在线主机全部瘫痪...");
                return false;
            }
           
            *m=(&machines[online[0]]);
            int min_load=(*m)->Load();
            *id=online[0];

            for(int i=1;i<num;i++)
            {
                
                if(min_load>machines[online[i]].Load())
                {
                   
                    *id=online[i];
                    min_load=machines[online[i]].Load();
                   
                }

              
            }
            *m=&machines[*id];
            mtx.unlock();
            return true;
            
        }


        void OfflineMachine(int which)
        {
            mtx.lock();
            for(auto iter = online.begin(); iter != online.end(); iter++)
            {
                if(*iter == which)
                {
                    machines[which].ResetLoad();
                    //要离线的主机已经找到啦
                    online.erase(iter);
                    offline.push_back(which);
                    break; //因为break的存在，所有我们暂时不考虑迭代器失效的问题
                }
            }
            mtx.unlock();
        }


        void OnlineMachine()
        {
            //我们统一上线，后面统一解决
            mtx.lock();
            online.insert(online.end(), offline.begin(), offline.end());
            offline.erase(offline.begin(), offline.end());
            mtx.unlock();

          INFO_Log("所有主机上线成功");
        }



        void ShowMachines()
        {
             mtx.lock();
             std::cout << "当前在线主机列表: ";
             for(auto &id : online)
             {
                 std::cout << id << " ";
             }
             std::cout << std::endl;
             std::cout << "当前离线主机列表: ";
             for(auto &id : offline)
             {
                 std::cout << id << " ";
             }
             std::cout << std::endl;
             mtx.unlock();
        }
   
    };


   

    class Control
    {
    private:
    Model model;
    View view;
    LoadBlance load;
       
    public:
        void RecoveryMachine()
        {
            load.OnlineMachine();
        }
        bool AllQuestions(string *html)
        {
            vector<struct Question> v1;
            
            if(model.GetAllQuestions(&v1))
            {
                sort(v1.begin(), v1.end(), [](const struct Question &q1, const struct Question &q2){
                    return atoi(q1.number.c_str()) < atoi(q2.number.c_str());
                });
                view.AllExpendHtml(v1,html);
                return true;
            }
            else
            {
                *html="形成题目列表失败";
                return false;
            }
        }
        bool Question(const string &number, string *html)
        {
            struct Question q;
            if(model.GetOneQuestion(number,&q))
            {
                view.OneExpendHtml(q,html);
                return true;
            }
            else
            {
                *html="获取: ";
                *html+=number;
                *html+="题目失败";
                return false;
            }
        }

        // code: #include...
        // input: ""
        void Judge(const std::string &number, const std::string in_json, std::string *out_json)
        {   
            //1.获取题目
            struct Question q;
           bool ret= model.GetOneQuestion(number,&q);
           if(!ret)
           {
                ERR_Log("获取：%s题目错误",number.c_str());
                return ;
           }

           //2.反序列化输入的请求
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);

            std::string code=in_value["code"].asCString();
            code+="\n";
            code+=q.tail;
            std::string input=in_value["input"].asCString();

            //3.序列化输出的请求
            Json::Value compile_value;
            compile_value["code"]=code;
            compile_value["input"]=input;
            compile_value["cpu_limit"]=q.cpu_limit;
            compile_value["mem_limit"]=q.mem_limit;

            std::string compile_string;
            Json::FastWriter writer;
            compile_string=writer.write(compile_value);


            //4.选择负载低的主机
            while(true)
            {
                int id=0;
                Machine *m;
                if(!load.SmartChoice(&id,&m))
                {
                    break;
                }
                Client cli(m->ip, m->port);
                m->IncLoad();
                INFO_Log(" 选择主机成功, 主机id: %d",id);
                cout << " 详情: " << m->ip << ":" << m->port << " 当前主机的负载是: " << m->Load() << "\n";
                if(auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    // 5. 将结果赋值给out_json
                    if(res->status == 200)
                    {
                        *out_json = res->body;
                        m->DecLoad();
                       INFO_Log("请求编译和运行服务成功...");
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    //请求失败
                    ERR_Log("当前请求的主机id: %d",id);
                    std::cout<< " 详情: " << m->ip << ":" << m->port << " 可能已经离线"<< "\n";
                    load.OfflineMachine(id);
                    load.ShowMachines(); //仅仅是为了用来调试
                }
            }

            


            



        }

        Control()
        {}
        ~Control()
        {}
    };
    
   
    
}